"""
filename: __init__.py

All core audio-related operations
"""
from pydub import AudioSegment
import os
from pathlib import Path
from .color import *
from .qt_signal import SIGNAL
from typing import List


def debug(text):
    print(text)
    SIGNAL.text_print.emit(text)


def any_to_wav(file: str, save_name: str):
    """Convert anyfile to wav format using ffmpeg, not very practical.

    Args:
        file (str): the file to be converted
        save_name (str): save to which. **DO NOT ADD .wav EXT NAME**
    """
    c = os.system(f'ffmpeg -i {file} -f wav {save_name}.wav')
    if c == 0:
        print(G(f"[converter] Success convert {file} to wav format"))
    else:
        print(R(f"[converter] Error converting {file} to wav format"))


def robust_load_audio(file: str) -> AudioSegment:
    """Load audio file, and handle error, from any format audio.

    Args:
        file (str): the file to be loaded
    Returns:
        AudioSegment: the loaded audio
    """
    if not Path(file).exists():
        print(R(f"[loader] Error: '{Y(file)}' does not exists"))
        SIGNAL.text_print.emit(f'{file} does not exists')
        return None
    try:
        return AudioSegment.from_file(file)
    except Exception as e:
        print(f"{R('[loader]')} Error when load {Y(file)}:\n\n{Y(e)}\n\n")
        return None


def match_target_amplitude(sound: AudioSegment, target_dBFS: float) -> AudioSegment:
    """Match the amplitude of the sound to the target dBFS

    Args:
        sound (AudioSegment): input original sound
        target_dBFS (float): target amplitude.

    Returns:
        AudioSegment
    """
    if sound is None:
        print(R('[loudness match] Error: Got none instead of AudioSegment!'))
        return None
    debug(
        f'[loudness match] match {sound.dBFS:.2f} to {target_dBFS}, gain {target_dBFS - sound.dBFS}')
    change_in_dBFS = target_dBFS - sound.dBFS
    return sound.apply_gain(change_in_dBFS)


def make_audio_with_bgm(top_audio: AudioSegment,
                        bg_audio: AudioSegment,
                        top_dBFS=-12.0,
                        bgm_dBFS=-22.0) -> AudioSegment:
    """Add bgm to top audio

    The bgm will be trimmed to match the length of the top audio,
    if bgm length is shorter than top audio, the bgm will be repeated.

    Args:
        top_audio (AudioSegment): the top layer audio needed to add bgm
        bg_audio (AudioSegment): the BGM
        top_dBFS (float): the target amplitude of the top audio
        bgm_dBFS (float): the target amplitude of the bgm
    """
    if top_audio is None or bg_audio is None:
        print(R('[make bgm] Error: Got None instead of AudioSegment!'))
        return None
    top_audio = match_target_amplitude(top_audio, top_dBFS)
    bg_audio = match_target_amplitude(bg_audio, bgm_dBFS)
    if bg_audio.duration_seconds < top_audio.duration_seconds:
        times = int((top_audio.duration_seconds -
                    bg_audio.duration_seconds) / bg_audio.duration_seconds) + 2
        # repeat the bgm
        bg_audio = bg_audio * times
        debug(f'[make bgm] bgm too short, repeat bgm {times} times')
    # trim b to match t
    bg_audio = bg_audio[:len(top_audio)]
    # fade_in fade_out
    bg_audio = bg_audio.fade_in(1000).fade_out(1000)
    debug('[make bgm] composing bgm...')
    # overlay t on b
    return bg_audio.overlay(top_audio, position=0)


def make_audio_with_bgm_and_export(top_audio: str, bgm: str, file: str) -> None:
    """Over lay the top audio with bgm and export to file. smart change volume

    Args:
        top_audio (str): the top audio file path name.
        bgm (str): the bgm.
        file (str): which file to export. the format is WAV
    """
    debug(f'[compose] {top_audio} + {bgm} to {file}')
    p1 = make_audio_with_bgm(robust_load_audio(top_audio),
                             robust_load_audio(bgm))
    p1.export(file, format='wav')


def concat_audio_files(files: List[str], save_name: str) -> None:
    """Concat all files to one file. This is **file based**, not segment based.

    Args:
        files (List[str]): any audio file
        save_name (str): the name of the file
    """
    if len(files) == 0:
        print(R('[concat] Error: No file to concat!'))
        return
    debug(f'[concat] concat {len(files)} files to {save_name}')
    audio = AudioSegment.empty()
    for f in files:
        a = robust_load_audio(f)
        audio += a
    audio.export(save_name, format='wav')


def concat_audio_files_segment(audios: List[AudioSegment]) -> AudioSegment:
    """Concat all files to one file, this is **AudioSegment object based**.

    Args:
        audios (List[AudioSegment]): all audio segments

    Returns:
        AudioSegment: concated audio
    """
    a = AudioSegment.empty()
    for audio in audios:
        a += audio
    return a


def save_to_file(audio: AudioSegment, file: str):
    debug(f'[save] saving file to "{file}"')
    audio.export(file, format='wav')


def cvt_any_to_wav(filename, save_to):
    """Convert any file to wav format

    Args:
        filename (str): the filename to convert
        save_to (str): the filename to save, must end with .wav
    """
    debug(f'Converting {filename} to wav')
    robust_load_audio(filename).export(save_to, format='wav')


def cvt_any_to_mp3(filename, save_to):
    """convert any file to mp3 format

    Args:
        filename (str): the filename to convert
        save_to (str): the filename to save, must end with .mp3
    """
    debug(f'Converting {filename} to mp3')
    robust_load_audio(filename).export(save_to, format='mp3')


def classic_compose_boat(audio_files: List[str],
                         bgm: str,
                         end_music: str,
                         save_to: str):
    """Classic music compose function.

    First, concat all audios to one file.
    Then, add the BGM to it, and make sure the length is proper.
    Finally, compose the end_music to the end of the audio

    Args:
        audio_folder (List[str]): all the audio files
        bgm (str): which bgm to compose
        end_music (str): use which music to compose in the end
        save_to (str): the filename to save
    """
    debug('[composer] concating all audio')
    track1 = concat_audio_files_segment(
        [robust_load_audio(f) for f in audio_files])
    debug('[composer] composing BGM')
    track2 = make_audio_with_bgm(track1, robust_load_audio(bgm), bgm_dBFS=-25.0)
    end = robust_load_audio(end_music)
    debug('[composer] adding end music')
    end = match_target_amplitude(end, -12.0)
    final = concat_audio_files_segment([track2, end])
    debug('[composer] saving file')
    save_to_file(final, save_to)

# p1 = make_audio_with_bgm(robust_load_audio('第一段.m4a'),
#                         robust_load_audio('bgm1.mp3'))
# p1.export('第一段_with_bgm.wav', format='wav')

# p2 = make_audio_with_bgm(robust_load_audio('第二段.m4a'),
#                         robust_load_audio('bgm2.mp3'))
# p2.export('第二段_with_bgm.wav', format='wav')
